/*  mudmap - A tool to create and organize user side maps for MUD games
 *  Copyright (C) 2012  Neop (aka moemneop, email: mneop@web.de)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include "dia_mapdrawing.h"

namespace dia{

bool *mapdrawing::mode_debug = nullptr;
bool *mapdrawing::mode_small_tiles = nullptr;
bool *mapdrawing::mode_aspect_ratio = nullptr;
bool *mapdrawing::mode_way_lines_show = nullptr;
bool *mapdrawing::mode_way_lines_curved = nullptr;
shared_ptr<mapdrawing> *mapdrawing::cur_mapd = nullptr;

/// --------------------------------- Konstruktor / Destruktor / Initialisierung ------------------------
mapdrawing::mapdrawing(string file, GtkWidget *notebook, GtkWidget *window){
    this->notebook = notebook;
    this->window = window;

    close = false;
    save_cur_screen_as_png = false;
    mouse_l = mouse_r = false;
    mouse_x_bef = mouse_y_bef = -1;
    content = nullptr;
    way_connect_pl1 = nullptr;
    cur_center_x = cur_center_y = 0;
    copy_layer = shared_ptr<layer>(new layer(-1));

    cur_world = worldmgr::get_instance()->get_world(file);
    goto_home(false);
    place_call_list.push_back(coord(cur_layer, cur_center_x, cur_center_y));
    place_call_list_num = place_call_list.end();

    load_meta();

    if(cur_world == nullptr){
        label = gtk_label_new(_("Couldn't load world"));
        gtk_widget_show(label);
        content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
        gtk_widget_show(content);
    } else init_gui();
}

mapdrawing::~mapdrawing(){
}

void mapdrawing::save_meta(){
    string f = cur_world->file + "_meta";
    ofstream ofs(f, ios::trunc);
    if(ofs.is_open()){
        ofs << "// lp layer x y => last shown position of the map (place in center)" << endl << "lp " << cur_layer << " " << (float) cur_center_x << " " << (float) cur_center_y << endl;
        for(list<coord>::iterator it = place_call_list.begin(); it != place_call_list.end(); it++) ofs << "pcv " << it->layer << " " << it->x << " " << it->y << endl;
        ofs.close();
    } else {
        GtkWidget *dlg = gtk_message_dialog_new(GTK_WINDOW(window), GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Couldn't save world meta file (%s)"), f.c_str());
        gtk_widget_show_all(dlg);
        gtk_dialog_run(GTK_DIALOG(dlg));
        gtk_widget_destroy(dlg);
    }
}

void mapdrawing::load_meta(){
    cur_center_x = cur_center_y = 0;
    string f = cur_world->file + "_meta";
    vector<string> lines = readfile_sv(f, true);
    for(unsigned i = 0; i < lines.size(); i++){
        if(config_comp(lines[i], "lp")){
            string tmp;
            stringstream ss(lines[i]);
            ss >> tmp >> cur_layer >> cur_center_x >> cur_center_y;
        } else if(config_comp(lines[i], "pcv")){
            stringstream ss(lines[i]);
            string tmp;
            int lay;
            float x, y;
            ss >> tmp >> lay >> x >> y;
            place_call_list_add(lay, x, y);
        }
    }
}

void mapdrawing::init_gui(){
    label = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_box_pack_start(GTK_BOX(label), gtk_label_new(cur_world->name.c_str()), true, true, 0);
    GtkWidget *close_button = gtk_button_new();
    gtk_button_set_image(GTK_BUTTON(close_button), gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
    gtk_box_pack_start(GTK_BOX(label), close_button, true, true, 0);
    gtk_widget_show_all(label);
    g_signal_connect(G_OBJECT(close_button), "clicked", G_CALLBACK(close_func), this);

    content = gtk_drawing_area_new();
    gtk_widget_show(content);
    gtk_widget_set_size_request(content, 750, 500);
    gtk_widget_set_has_tooltip(content, true);
    gtk_widget_add_events(content, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
    g_signal_connect(G_OBJECT(content), "draw", G_CALLBACK(drawfunc), (void*) this);
    g_signal_connect(G_OBJECT(content), "button_press_event", G_CALLBACK(buttonpressfunc), (void*) this);
    g_signal_connect(G_OBJECT(content), "button_release_event", G_CALLBACK(buttonreleasefunc), (void*) this);
    g_signal_connect(G_OBJECT(content), "motion_notify_event", G_CALLBACK(pointermotionfunc), (void*) this);
    g_signal_connect(G_OBJECT(content), "query-tooltip", G_CALLBACK(tooltipfunc), (void*) this);
}

void mapdrawing::show_context_menu(GdkEventButton *event){
    for(unsigned i = 0; i < show_context_menu_dir_vec.size(); i++) delete show_context_menu_dir_vec[i];
    show_context_menu_dir_vec.clear();
    pair<int, int> tile_pos = get_tile_pos(event->x, event->y);
    shared_ptr<place> pl = cur_world->get_layer(cur_layer)->get(tile_pos.first, tile_pos.second);
    context_tile_x = tile_pos.first;
    context_tile_y = tile_pos.second;
    context_place = pl;

    GtkWidget *menu_colrow = gtk_menu_new();
    GtkWidget *mi_colrow = gtk_menu_item_new_with_label(_("Column / row"));
    //GtkWidget *mi_colrow_insert_row = gtk_menu_item_new_with_label(_("(-) Insert row"));
    //gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi_colrow_insert_row), gtk_image_new_from_stock(GTK_STOCK_ADD, GtkIconSize(-1)));
    GtkWidget *mi_colrow_insert_row = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, nullptr);
    GtkWidget *mi_colrow_insert_column = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, nullptr);
    GtkWidget *mi_colrow_remove_row = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, nullptr);
    GtkWidget *mi_colrow_remove_column = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, nullptr);

    gtk_menu_item_set_label(GTK_MENU_ITEM(mi_colrow_insert_row), _("(-) Insert row"));
    gtk_menu_item_set_label(GTK_MENU_ITEM(mi_colrow_insert_column), _("(|) Insert column"));
    gtk_menu_item_set_label(GTK_MENU_ITEM(mi_colrow_remove_row), _("(-) Remove row"));
    gtk_menu_item_set_label(GTK_MENU_ITEM(mi_colrow_remove_column), _("(|) Remove column"));

    gtk_menu_shell_append(GTK_MENU_SHELL(menu_colrow), mi_colrow_insert_column);
    gtk_menu_shell_append(GTK_MENU_SHELL(menu_colrow), mi_colrow_insert_row);
    gtk_menu_shell_append(GTK_MENU_SHELL(menu_colrow), mi_colrow_remove_column);
    gtk_menu_shell_append(GTK_MENU_SHELL(menu_colrow), mi_colrow_remove_row);
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi_colrow), menu_colrow);

    g_signal_connect(mi_colrow_insert_column, "activate", G_CALLBACK(mi_insert_column_func), (gpointer) ((long)context_tile_x));
    g_signal_connect(mi_colrow_insert_row, "activate", G_CALLBACK(mi_insert_row_func), (gpointer) ((long)context_tile_y));
    g_signal_connect(mi_colrow_remove_column, "activate", G_CALLBACK(mi_remove_column_func), (gpointer) ((long)context_tile_x));
    g_signal_connect(mi_colrow_remove_row, "activate", G_CALLBACK(mi_remove_row_func), (gpointer) ((long)context_tile_y));

    GtkWidget *menu = gtk_menu_new();
    if(pl != nullptr){
        vector<pair<string, shared_ptr<place> > > ways = cur_world->get_ways(pl);

        GtkWidget *mi_edit = gtk_image_menu_item_new_from_stock(GTK_STOCK_EDIT, nullptr);
        GtkWidget *mi_edit_marked = gtk_image_menu_item_new_from_stock(GTK_STOCK_EDIT, nullptr);
        gtk_menu_item_set_label(GTK_MENU_ITEM(mi_edit_marked), _("Edit marked"));
        GtkWidget *mi_cut = gtk_image_menu_item_new_from_stock(GTK_STOCK_CUT, nullptr);
        GtkWidget *mi_delete = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, nullptr);
        GtkWidget *mi_edit_comments = gtk_image_menu_item_new_from_stock(GTK_STOCK_EDIT, nullptr);
        GtkWidget *mi_edit_area = gtk_image_menu_item_new_from_stock(GTK_STOCK_EDIT, nullptr);
        GtkWidget *mi_sub_area = gtk_menu_item_new_with_label(_("Sub area"));
        GtkWidget *mi_sa_new = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, nullptr);
        GtkWidget *mi_sa_new_with_layer = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, nullptr);
        GtkWidget *mi_sa_remove = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, nullptr);
        gtk_menu_item_set_label(GTK_MENU_ITEM(mi_sa_new_with_layer), _("Connect with a new place on a new layer"));
        gtk_menu_item_set_label(GTK_MENU_ITEM(mi_edit_comments), _("Edit comments / items"));
        gtk_menu_item_set_label(GTK_MENU_ITEM(mi_edit_area), _("Edit area"));

        GtkWidget *menu_sub_area = gtk_menu_new();
        GtkWidget *mi_ways = gtk_menu_item_new_with_label(_("Ways / Exits"));
        GtkWidget *mi_ways_connect = gtk_image_menu_item_new_from_stock(GTK_STOCK_CONNECT, nullptr);
        GtkWidget *mi_ways_remove = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, nullptr);
        GtkWidget *menu_ways = gtk_menu_new();
        GtkWidget *menu_ways_remove = gtk_menu_new();
        GtkWidget *menu_ways_connect = gtk_menu_new();
        GtkWidget *menu_sa_remove = gtk_menu_new();

        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_edit);
        if(copy_layer->id != -1) gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_edit_marked);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_cut);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_delete);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_edit_comments);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_edit_area);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_colrow);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_ways);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_sub_area);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu_sub_area), mi_sa_new);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu_sub_area), mi_sa_new_with_layer);
        if(pl->children.size()){
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_sub_area), mi_sa_remove);
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_sub_area), gtk_separator_menu_item_new());
        }
        gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways), mi_ways_connect);
        if(ways.size()){
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways), mi_ways_remove);
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways), gtk_separator_menu_item_new());
        }

        gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi_sub_area), menu_sub_area);
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi_sa_remove), menu_sa_remove);
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi_ways_remove), menu_ways_remove);
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi_ways_connect), menu_ways_connect);
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi_ways), menu_ways);

        // --------------------------- sub areas ------------------------------
        for(unsigned i = 0; i < pl->children.size(); i++){
            char str[128];
            sprintf(str, _("go to %s"), cur_world->get_place(pl->children[i])->name.c_str());
            GtkWidget *tmp_w = gtk_menu_item_new_with_label(str);
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_sub_area), tmp_w);
            g_signal_connect(tmp_w, "activate", G_CALLBACK(mi_subarea_goto_func), (void*) ((long)pl->children[i]));

            GtkWidget *tmp = gtk_menu_item_new_with_label(cur_world->get_place(pl->children[i])->name.c_str());
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_sa_remove), tmp);
            g_signal_connect(tmp, "activate", G_CALLBACK(mi_subarea_remove_func), (void*) ((long)pl->children[i]));
        }

        // ---------------------------- ways ---------------------------------
        for(unsigned i = 0; i < ways.size(); i++){
            stringstream ss;
            ss << " [" << ways[i].first << "] " << (ways[i].second ? ways[i].second->name : _("ERROR: not available")); // go to
            GtkWidget *mi_cur_way = gtk_menu_item_new_with_label(ss.str().c_str());
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways), mi_cur_way);
            if(ways[i].second) g_signal_connect(mi_cur_way, "activate", G_CALLBACK(mi_subarea_goto_func), (void*) ((long)ways[i].second->id));

            GtkWidget *mi_cur_way2 = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, nullptr); // remove
            gtk_menu_item_set_label(GTK_MENU_ITEM(mi_cur_way2), ss.str().c_str());
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways_remove), mi_cur_way2);
            show_context_menu_dir_vec.push_back(new char*((char*)ways[i].first.c_str()));
            g_signal_connect(mi_cur_way2, "activate", G_CALLBACK(mi_way_remove_func), show_context_menu_dir_vec.back());
        }

        // -------------------------------- add ways -----------------------------------------------
        vector<string> ways_connected; // schon verbundene Richtungen
        for(map<string, int>::iterator it = pl->ways.begin(); it != pl->ways.end(); it++) ways_connected.push_back(it->first);

        for(map<string, string>::iterator it = world::way_names.begin(); it != world::way_names.end(); it++){
            if(pl->ways.count(it->first)) continue;
            GtkWidget *mi_ways_cnt = gtk_image_menu_item_new_from_stock(GTK_STOCK_CONNECT, nullptr); // connect
            gtk_menu_item_set_label(GTK_MENU_ITEM(mi_ways_cnt), world::way_str_mnemonic[it->first].c_str());
            gtk_menu_item_set_use_underline(GTK_MENU_ITEM(mi_ways_cnt), true);
            gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways_connect), mi_ways_cnt);
            show_context_menu_dir_vec.push_back(new char*((char*)it->first.c_str()));
            g_signal_connect(mi_ways_cnt, "activate", G_CALLBACK(mi_way_add_func), show_context_menu_dir_vec.back());
        }
        gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways_connect), gtk_separator_menu_item_new());
        GtkWidget *mi_ways_cnt = gtk_menu_item_new_with_label(_("another"));
        gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways_connect), mi_ways_cnt);
        g_signal_connect(mi_ways_cnt, "activate", G_CALLBACK(mi_way_add_another_func), this);
        mi_ways_cnt = gtk_menu_item_new_with_label(_("_automatic"));
        gtk_menu_item_set_use_underline(GTK_MENU_ITEM(mi_ways_cnt), true);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu_ways_connect), mi_ways_cnt);
        g_signal_connect(mi_ways_cnt, "activate", G_CALLBACK(mi_way_add_automatic_func), this);

        // -----------------------------------------------------------------------------------------

        g_signal_connect(mi_edit, "activate", G_CALLBACK(mi_place_edit_func), this);
        g_signal_connect(mi_edit_marked, "activate", G_CALLBACK(mi_place_edit_marked_func), this);
        g_signal_connect(mi_delete, "activate", G_CALLBACK(mi_place_delete_func), this);
        g_signal_connect(mi_cut, "activate", G_CALLBACK(mi_place_cut_func), this);
        g_signal_connect(mi_edit_comments, "activate", G_CALLBACK(mi_comments_edit_func), this);
        g_signal_connect(mi_edit_area, "activate", G_CALLBACK(mi_area_edit_func), this);
        g_signal_connect(mi_sa_new, "activate", G_CALLBACK(mi_subarea_new_func), this);
        g_signal_connect(mi_sa_new_with_layer, "activate", G_CALLBACK(mi_subarea_new_with_layer_func), this);
    } else {
        GtkWidget *mi_paste = nullptr;
        GtkWidget *mi_new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, nullptr);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_new);
        g_signal_connect(mi_new, "activate", G_CALLBACK(mi_place_new_func), this);

        if(copy_layer->id != -1){
            mi_paste = gtk_image_menu_item_new_from_stock(GTK_STOCK_PASTE, nullptr);
            gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_paste);
            g_signal_connect(mi_paste, "activate", G_CALLBACK(mi_paste_place_func), this);
        }

        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi_colrow);
    }

    gtk_widget_show_all(menu);
    gtk_menu_popup(GTK_MENU(menu), nullptr, nullptr, nullptr, nullptr, (event != nullptr) ? event->button : 0, gdk_event_get_time((GdkEvent*) event));
}

/// -----------------------------------------------------------------------------------------------------

/// --------------------------------- Zeichnen ----------------------------------------------------------
gboolean mapdrawing::drawfunc(GtkWidget *widget, cairo_t *cr, gpointer data){
    mapdrawing *mapd = (mapdrawing*) data;

    int w = gtk_widget_get_allocated_width(widget);
    int h = gtk_widget_get_allocated_height(widget);
    mapd->draw(cr, w, h, mapd->cur_center_x, mapd->cur_center_y);
    return true;
}

unsigned mapdrawing::get_tile_width(){
    unsigned w = properties::tile_size_square;
    if(*mode_aspect_ratio) w = properties::tile_size_width;
    if(*mode_small_tiles) w /= 2;
    return w;
}

unsigned mapdrawing::get_tile_height(){
    unsigned h = properties::tile_size_square;
    if(*mode_aspect_ratio) h = properties::tile_size_height;
    if(*mode_small_tiles) h /= 2;
    return h;
}

void mapdrawing::draw(cairo_t *cr, guint width, guint height, float cur_center_x, float cur_center_y){
    cairo_text_extents_t te;

    unsigned tile_width = get_tile_width();
    unsigned tile_height = get_tile_height();

    wdtw = (float) width / tile_width;
    hdth = (float) height / tile_height;
    float wdtwd2 = wdtw / 2;
    float hdthd2 = hdth / 2;

    shared_ptr<place> cur_place = nullptr;
    color col;

    // Speichert, ob an der Position ein Ort gezeichnet ist
    unsigned char **place_stencil = nullptr;
    cairo_surface_t *cs2 = nullptr;
    cairo_t *cr2 = nullptr;
    // ... ob eine Linie schon gezeichnet wurde
    list<unsigned long> drawn_ways;
    hash<string> hash_str;

    if(*mode_way_lines_show){
        place_stencil = (unsigned char**) calloc((unsigned) (wdtw + 3), sizeof(unsigned char*));
        for(unsigned i = 0; i <= wdtw + 2; i++) place_stencil[i] = (unsigned char*) calloc((hdth + 2) / 8 + 1, sizeof(unsigned char));
        #ifdef __linux__
        cs2 = cairo_surface_create_similar_image(cairo_get_target(cr), CAIRO_FORMAT_ARGB32, width, height);
        #else
        cs2 = cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, width, height);
        #endif
        cr2 = cairo_create(cs2);
        cairo_set_line_width(cr2, 5);
        //cairo_set_source_rgb(cr2, cur_world->way_col.get_flt_r(), cur_world->way_col.get_flt_g(), cur_world->way_col.get_flt_b()); // nach unten verlegt
    }

    for(int txf = -1; txf < wdtw + 1; txf++){
        int tx = txf - tk::round_to_int(cur_center_x);
        for(int tyf = -1; tyf < hdth + 1; tyf++){
            int ty = tyf - tk::round_to_int(cur_center_y);

            cur_place = cur_world->get_place(tx - tk::round_down(wdtwd2), -ty + tk::round_down(hdthd2), cur_layer);

            if(cur_place != nullptr){
                int posx = (float)(tx + cur_center_x + tk::round_r(wdtwd2) - 0.5) * tile_width;
                int posy = (float)(ty + cur_center_y + tk::round_r(hdthd2) - 0.5) * tile_height;

                // lines of ways
                if(*mode_way_lines_show){
                    for(map<string, int>::iterator it = cur_place->ways.begin(); it != cur_place->ways.end(); it++){
                        shared_ptr<place> plex = cur_world->get_place(it->second);
                        string plexit = plex->get_exit_to_place(cur_place->id);
                        bool br = false; // Abbruch?
                        if(cur_place->layer != plex->layer) br = true; // || !place::get_exit_default(it->first) || !place::get_exit_default(plexit)) br = true;

                        // wurde der Weg schon gezeichnet?
                        unsigned long foo = cur_place->id | (hash_str(it->first) << (sizeof(int) << 2));
                        for(list<unsigned long>::iterator it = drawn_ways.begin(); it != drawn_ways.end(); it++) if(*it == foo){
                            br = true;
                            break;
                        }
                        if(!br){
                            drawn_ways.push_back(it->second | (hash_str(plexit) << (sizeof(int) << 2)));

                            int exit_posx = posx;
                            int exit_posy = posy;
                            //bool exit_pos_default = true;
                            //get_exit_pos(it->first, &exit_posx, &exit_posy);
                            bool exit_pos_default = get_exit_pos(it->first, &exit_posx, &exit_posy);
                            cairo_move_to(cr2, exit_posx, exit_posy);

                            int a = plex->posx - cur_place->posx;
                            int b = plex->posy - cur_place->posy;

                            int exit_posx2 = posx + a * tile_width;
                            int exit_posy2 = posy - b * tile_height;
                            //exit_pos_default = exit_pos_default && place::get_exit_default(plexit); // alternativ zum zusammengesetzten Befehl:
                            exit_pos_default = get_exit_pos(plexit, &exit_posx2, &exit_posy2) && exit_pos_default;

                            if(exit_pos_default) cairo_set_source_rgb(cr2, cur_world->way_col.get_flt_r(), cur_world->way_col.get_flt_g(), cur_world->way_col.get_flt_b());
                            else cairo_set_source_rgb(cr2, cur_world->way_ndefault_col.get_flt_r(), cur_world->way_ndefault_col.get_flt_g(), cur_world->way_ndefault_col.get_flt_b());

                            if(*mode_way_lines_curved && (a * a + b * b) > 2){
                                a *= tile_width >> 1;
                                b *= tile_height >> 1;
                                float dist = sqrt(a * a + b * b) * (mode_small_tiles ? 0.5 : 1);
                                pair<float, float> normals[2] = {get_exit_normal(it->first), get_exit_normal(plexit)};
                                cairo_curve_to(cr2, exit_posx - dist * normals[0].first, exit_posy - dist * normals[0].second, exit_posx2 - dist * normals[1].first, exit_posy2 - dist * normals[1].second, exit_posx2, exit_posy2);
                            }
                            else cairo_line_to(cr2, exit_posx2, exit_posy2);
                            cairo_stroke(cr2);
                        }
                    }
                }

                // area color
                if(cur_place->area != -1){
                    col = cur_world->get_area(cur_place->area)->col;
                    cairo_set_source_rgb(cr, col.get_flt_r(), col.get_flt_g(), col.get_flt_b());
                    cairo_rectangle(cr, posx, posy, tile_width + 0.5, tile_height);
                    cairo_fill(cr);
                }

                // Farbe im Mittelpunkt eines Tiles
                cairo_set_source_rgb(cr, (float) 207 / 255, (float) 190 / 255, (float) 134 / 255);
                cairo_rectangle(cr, posx + properties::tile_dl_border_dist, posy + properties::tile_dl_border_dist, tile_width - 2 * properties::tile_dl_border_dist, tile_height - 2 * properties::tile_dl_border_dist);
                cairo_fill(cr);

                // danger level color
                col = color(cur_world->get_danger_level_col(cur_place->danger_lvl));
                cairo_set_source_rgb(cr, col.get_flt_r(), col.get_flt_g(), col.get_flt_b());
                cairo_set_line_width(cr, properties::tile_dl_line_width);
                cairo_rectangle(cr, posx + properties::tile_dl_border_dist, posy + properties::tile_dl_border_dist, tile_width - 2 * properties::tile_dl_border_dist, tile_height - 2 * properties::tile_dl_border_dist);
                cairo_stroke(cr);

                // exits
                cairo_set_source_rgb(cr, cur_world->way_col.get_flt_r(), cur_world->way_col.get_flt_g(), cur_world->way_col.get_flt_b());
                for(map<string, int>::iterator it = cur_place->ways.begin(); it != cur_place->ways.end(); it++){
                    int x = 0, y = 0;
                    bool default_exit = get_exit_pos(it->first, &x, &y);
                    if(default_exit){
                        x += posx;
                        y += posy;
                        cairo_arc(cr, x, y, properties::exit_radius, 0, 7);
                        cairo_fill(cr);
                    }
                }

                cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
                if(!*mode_small_tiles){
                    double text_y_dif = 0;

                    // Text: Name
                    cairo_select_font_face(cr, "Lucida", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
                    cairo_set_font_size(cr, properties::tile_text_size);
                    //cairo_text_extents(cr, "test", &te);
                    string name = cur_place->name.c_str();
                    cairo_text_extents(cr, name.c_str(), &te);

                    //const double text_y_dist = te.height * 1.5;
                    const double text_y_dist = 10.0 * 1.5;

                    cairo_move_to(cr, posx + properties::tile_text_border_dist - te.x_bearing, posy + properties::tile_text_border_dist - te.y_bearing);
                    // cut the name if it is too long
                    bool name_is_too_long = !*mode_aspect_ratio && te.width > (get_tile_width() - 2 * properties::tile_dl_border_dist);
                    if(name_is_too_long){
                        cairo_show_text(cr, name.substr(0, 12).c_str());
                        name.erase(0, 12);
                        text_y_dif += text_y_dist;
                        cairo_move_to(cr, posx + properties::tile_text_border_dist - te.x_bearing, posy + properties::tile_text_border_dist - te.y_bearing + text_y_dif);
                    }
                    cairo_show_text(cr, name.c_str());
                    cairo_select_font_face(cr, "Lucida", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);

                    // Text: rec lvl
                    stringstream rl;
                    bool show_rl = true;
                    rl << "Rec level: ";
                    if(cur_place->rec_lvl_min != -1){
                        if(cur_place->rec_lvl_max != -1) rl << cur_place->rec_lvl_min << " - " << cur_place->rec_lvl_max;
                        else rl << cur_place->rec_lvl_min << " - ?";
                    } else if(cur_place->rec_lvl_max != -1){
                        rl << "? - " << cur_place->rec_lvl_max;
                    } else show_rl = false;
                    if(show_rl){
                        text_y_dif += text_y_dist;
                        cairo_move_to(cr, posx + properties::tile_text_border_dist - te.x_bearing, posy + properties::tile_text_border_dist - te.y_bearing + text_y_dif);
                        //cairo_move_to(cr, posx + properties::tile_text_border_dist - te.x_bearing, posy + properties::tile_text_border_dist - te.y_bearing + te.height * 1.7);
                        cairo_show_text(cr, rl.str().c_str());
                    }

                    // Text: subareas
                    stringstream child_pl;
                    if(cur_place->children.size() == 1){
                        child_pl << "sa: " << cur_world->get_place(cur_place->children[0])->name;
                    } else if(cur_place->children.size() > 1) child_pl << "sa: " << cur_place->children.size();
                    if(child_pl.str() != ""){
                        text_y_dif += text_y_dist;
                        cairo_move_to(cr, posx + properties::tile_text_border_dist - te.x_bearing, posy + properties::tile_text_border_dist - te.y_bearing + text_y_dif);
                        //cairo_move_to(cr, posx + properties::tile_text_border_dist - te.x_bearing, posy + properties::tile_text_border_dist - te.y_bearing + te.height * 1.7 * (show_rl ? 2 : 1));
                        cairo_show_text(cr, child_pl.str().c_str());
                    }
                }

                string flags;
                if(cur_place->has_shop) flags += _("S");
                if(cur_place->has_teacher) flags += _("T");
                if(cur_place->has_free_food) flags += _("F");
                if(cur_place->has_free_beverages) flags += _("B");
                if(!cur_place->comments.empty()) flags += _("C");

                if(flags != ""){
                    cairo_text_extents(cr, properties::upstr, &te);
                    cairo_move_to(cr, posx + properties::tile_text_border_dist, posy + tile_height - properties::tile_text_border_dist);
                    cairo_show_text(cr, flags.c_str());
                }

                // draw characters u / d if a way leads up or down
                if(cur_place->ways.count("u") != 0){
                    cairo_set_font_size(cr, 20);
                    cairo_text_extents(cr, properties::upstr, &te);
                    cairo_move_to(cr, posx + tile_width - properties::tile_text_border_dist - te.width, posy + tile_height - properties::tile_text_border_dist);
                    cairo_show_text(cr, properties::upstr);
                }

                if(cur_place->ways.count("d") != 0){
                    cairo_set_font_size(cr, 20);
                    cairo_text_extents(cr, properties::downstr, &te);
                    cairo_move_to(cr, posx + tile_width - properties::tile_text_border_dist - ((cur_place->ways.count("u") != 0) ? 2 : 1) * te.width, posy + tile_height - properties::tile_text_border_dist);
                    cairo_show_text(cr, properties::downstr);
                }
                cairo_set_font_size(cr, properties::tile_text_size);

                // wenn der ort ausgeschnitten ist
                if(cur_layer == copy_layer->id && copy_layer->get(cur_place->posx, cur_place->posy) != nullptr){
                    cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
                    cairo_rectangle(cr, posx, posy, tile_width, tile_height);
                    cairo_fill(cr);
                }
                if(*mode_way_lines_show) place_stencil[txf + 1][(tyf + 1) / 8] |= (0x1 << ((tyf + 1) % 8));
            }
            // Positionen aller Tiles (auch ungezeichnete)
            if(*mode_debug){
                int posx = tk::round_down(tx - wdtwd2) + 1;
                int posy = -tk::round_down(ty - hdthd2) - 1;
                int posx2 = (float)(tx + cur_center_x + tk::round_r(wdtwd2) - 0.5) * tile_width;
                int posy2 = (float)(ty + cur_center_y + tk::round_r(hdthd2) - 0.5) * tile_height;
                cairo_move_to(cr, posx2 + properties::tile_text_border_dist - te.x_bearing, posy2 + properties::tile_text_border_dist - te.y_bearing + te.height * 1.7 * 2);
                stringstream ss;
                ss << posx << " " << posy;
                cairo_show_text(cr, ss.str().c_str());
            }
        }
    }

    // Maske für Linien von Wegen
    if(*mode_way_lines_show){ // Bedingung für Weglinien
        cairo_set_operator(cr2, CAIRO_OPERATOR_CLEAR);
        for(int txf = 0; txf <= wdtw + 1; txf++){
            int tx = txf - 1 - tk::round_to_int(cur_center_x);
            int posx = (float)(tx + cur_center_x + tk::round_r(wdtwd2) - 0.5) * tile_width;
            for(int tyf = 0; tyf <= hdth + 1; tyf++){
                int ty = tyf - 1 - tk::round_to_int(cur_center_y);
                int posy = (float)(ty + cur_center_y + tk::round_r(hdthd2) - 0.5) * tile_height;
                if(((place_stencil[txf][tyf / 8] & (0x1 << (tyf % 8))))){
                    cairo_rectangle(cr2, posx + properties::tile_dl_border_dist, posy + properties::tile_dl_border_dist, tile_width - 2 * properties::tile_dl_border_dist, tile_height - 2 * properties::tile_dl_border_dist);
                    cairo_fill(cr2);
                }
            }
        }
        cairo_set_source_surface(cr, cs2, 0, 0);
        cairo_paint(cr);
    }

    if(*mode_debug){
        stringstream ss, st, su, sv;
        ss << wdtw;
        cairo_move_to(cr, 10, 15);
        cairo_show_text(cr, ss.str().c_str());
        st << hdth;
        cairo_move_to(cr, 10, 30);
        cairo_show_text(cr, st.str().c_str());
        su << "mx " << cur_center_x;
        cairo_move_to(cr, 10, 45);
        cairo_show_text(cr, su.str().c_str());
        sv << "my " << cur_center_y;
        cairo_move_to(cr, 10, 60);
        cairo_show_text(cr, sv.str().c_str());
    }

    // ggf als png speichern
    if(save_cur_screen_as_png) cairo_surface_write_to_png(cairo_get_group_target(cr), image_file.c_str());

    if(*mode_way_lines_show){
        cairo_surface_destroy(cs2);
        cairo_destroy(cr2);
        for(unsigned i = 0; i <= wdtw + 1; i++) free(place_stencil[i]);
        free(place_stencil);
    }
}

bool mapdrawing::get_exit_pos(string dir, int *x, int *y){
    bool default_exit = true;
    if(dir == "n"){
        *x += get_tile_width() / 2;
        *y += properties::tile_dl_border_dist;
    } else if(dir == "e"){
        *x += get_tile_width() - properties::tile_dl_border_dist;
        *y += get_tile_height() / 2;
    } else if(dir == "s"){
        *x += get_tile_width() / 2;
        *y += get_tile_height() - properties::tile_dl_border_dist;
    } else if(dir == "w"){
        *x += properties::tile_dl_border_dist;
        *y += get_tile_height() / 2;
    } else if(dir == "ne"){
        *x += get_tile_width() - properties::tile_dl_border_dist;
        *y += properties::tile_dl_border_dist;
    } else if(dir == "se"){
        *x += get_tile_width() - properties::tile_dl_border_dist;
        *y += get_tile_height() - properties::tile_dl_border_dist;
    } else if(dir == "nw"){
        *x += properties::tile_dl_border_dist;
        *y += properties::tile_dl_border_dist;
    } else if(dir == "sw"){
        *x += properties::tile_dl_border_dist;
        *y += get_tile_height() - properties::tile_dl_border_dist;
    } else {
        *x += get_tile_width() / 2;
        *y += get_tile_height() / 2;
        default_exit = false;
    }
    return default_exit;
}

pair<float, float> mapdrawing::get_exit_normal(string dir){
    if(dir == "n") return pair<float, float>(0, 1);
    else if(dir == "e") return pair<float, float>(-1, 0);
    else if(dir == "s") return pair<float, float>(0, -1);
    else if(dir == "w") return pair<float, float>(1, 0);
    else if(dir == "ne") return pair<float, float>(-1, 1);
    else if(dir == "se") return pair<float, float>(-1, -1);
    else if(dir == "nw") return pair<float, float>(1, 1);
    else if(dir == "sw") return pair<float, float>(1, -1);
    return pair<float, float>(0, 0);
}

pair<int, int> mapdrawing::get_tile_pos(int mouse_x, int mouse_y){
    float tx = (float) mouse_x / get_tile_width();
    float ty = (float) mouse_y / get_tile_height();

    int tilex = tk::round_to_int(tx - wdtw / 2 - cur_center_x);
    int tiley = tk::round_to_int(-ty + hdth / 2 + cur_center_y);

    pair<int, int> ret(tilex, tiley);
    if(*mode_debug) cout << "tilepos " << ret.first << " " << ret.second << endl;
    return ret;
}

void mapdrawing::save_layer_as_png(string file, int layer){
    int x_max = cur_world->get_layer(layer)->x_max;
    int x_min = cur_world->get_layer(layer)->x_min;
    int y_max = cur_world->get_layer(layer)->y_max;
    int y_min = cur_world->get_layer(layer)->y_min;

    int w = (x_max - x_min + 1) * get_tile_width();
    int h = (y_max - y_min + 1) * get_tile_height();

    float x = (float) -(x_max + x_min) / 2;
    float y = (float) (y_max + y_min) / 2;

    /* // Area Legende schreiben...
    map<int, bool> areas;
    shared_ptr<layer> lay = (*cur_mapd)->cur_world->get_layer((*cur_mapd)->cur_layer);
    for(typename map<int, map<int, shared_ptr<place> > >::iterator itx = lay->data.begin(); itx != lay->data.end(); itx++){
        for(typename map<int, shared_ptr<place> >::iterator ity = itx->second.begin(); ity != itx->second.end(); ity++){
            if(ity->second != nullptr) areas[ity->second->area] = true;
        }
    }*/

    cairo_surface_t *surface;
    cairo_t *cr;
    surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
    cr = cairo_create(surface);

    draw(cr, w, h, x, y);

    cairo_surface_write_to_png(surface, file.c_str());

    cairo_destroy(cr);
    cairo_surface_destroy(surface);
}

void mapdrawing::save_screen_as_png(string file){
    save_cur_screen_as_png = true;
    image_file = file;
}

/// -----------------------------------------------------------------------------------------------------

/// --------------------------------- Maus --------------------------------------------------------------

gboolean mapdrawing::buttonpressfunc(GtkWidget *widget, GdkEventButton *event, gpointer data){
    if(event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS){
        pair<int, int> pos = (*cur_mapd)->get_tile_pos(event->x, event->y);
        shared_ptr<place> cpl = (*cur_mapd)->cur_world->get_layer((*cur_mapd)->cur_layer)->get(pos.first, pos.second);
        if(cpl == nullptr) (*cur_mapd)->paste_places(pos.first, pos.second);
        else if((*cur_mapd)->copy_layer->id == (*cur_mapd)->cur_layer){
            if(cpl == (*cur_mapd)->copy_layer->get(pos.first, pos.second)) (*cur_mapd)->copy_layer->remove(pos.first, pos.second);
            else (*cur_mapd)->cut_add_place(cpl);
        } else if((*cur_mapd)->copy_layer->id == -1) (*cur_mapd)->cut_add_place(cpl);
        (*cur_mapd)->mouse_l = (*cur_mapd)->mouse_r = false;
        (*cur_mapd)->draw();
    } else {
        if(event->button == 1) (*cur_mapd)->mouse_l = true;
        else if(event->button == 3){
            (*cur_mapd)->mouse_r = true;
            (*cur_mapd)->show_context_menu(event);
        }
    }
    return true;
}

gboolean mapdrawing::buttonreleasefunc(GtkWidget *widget, GdkEventButton *event, gpointer data){
    if(event->button == 1) (*cur_mapd)->mouse_l = false;
    else if(event->button == 3) (*cur_mapd)->mouse_r = false;
    (*cur_mapd)->mouse_x_bef = (*cur_mapd)->mouse_y_bef = -1;
    return true;
}

gboolean mapdrawing::pointermotionfunc(GtkWidget *widget, GdkEventMotion *event, gpointer data){
    if((*cur_mapd)->mouse_l){
        if((*cur_mapd)->mouse_x_bef != -1 && (*cur_mapd)->mouse_y_bef != -1){
            (*cur_mapd)->cur_center_x += (float) (event->x - (*cur_mapd)->mouse_x_bef) / (*cur_mapd)->get_tile_width();
            (*cur_mapd)->cur_center_y += (float) (event->y - (*cur_mapd)->mouse_y_bef) / (*cur_mapd)->get_tile_height();
            (*cur_mapd)->draw();
        }
        (*cur_mapd)->mouse_x_bef = event->x;
        (*cur_mapd)->mouse_y_bef = event->y;
    }
    return true;
}

gboolean mapdrawing::tooltipfunc(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data){
    pair<int, int> tile_pos = (*cur_mapd)->get_tile_pos(x, y);
    shared_ptr<place> cur_place = (*cur_mapd)->cur_world->get_place(tile_pos.first, tile_pos.second, (*cur_mapd)->cur_layer);
    // bei neuem place einmal false zurückgeben um neuen Tooltip zu erzeugen
    if(cur_place != nullptr && cur_place == (*cur_mapd)->tooltip_place){
        GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
        gtk_tooltip_set_custom(tooltip, box);

        stringstream ss;
        ss << cur_place->name;
        if(cur_place->area >= 0) ss << endl << cur_place->w->get_area(cur_place->area)->name;
        for(unsigned i = 0; i < cur_place->children.size(); i++){
            ss << endl << properties::arrow_dr << cur_place->w->get_place(cur_place->children[i])->name;
        }
        GtkWidget *label = gtk_label_new(ss.str().c_str());
        gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
        gtk_box_pack_start(GTK_BOX(box), label, true, true, 0);

        gtk_box_pack_start(GTK_BOX(box), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), true, true, 0);

        bool has_comment = false;
        stringstream comment_lines;

        for(list<string>::iterator itcomment = cur_place->comments.begin(); itcomment != cur_place->comments.end(); itcomment++){
            if(has_comment) comment_lines << endl;
            comment_lines << *itcomment;
            has_comment = true;
        }
        if(has_comment) gtk_box_pack_start(GTK_BOX(box), gtk_label_new(comment_lines.str().c_str()), true, true, 0);

        gtk_widget_show_all(box);

        //gtk_tooltip_set_text(GTK_TOOLTIP(tooltip), ss.str().c_str());
        return true;
    }
    (*cur_mapd)->tooltip_place = cur_place;
    return false;
}

/// -----------------------------------------------------------------------------------------------------

/// --------------------------------- get / set ---------------------------------------------------------

shared_ptr<world> mapdrawing::get_world(){
    return cur_world;
}

GtkWidget *mapdrawing::get_content(){
    return content;
}

GtkWidget *mapdrawing::get_label(){
    return label;
}

/// -----------------------------------------------------------------------------------------------------

/// --------------------------------- goto pos ----------------------------------------------------------

void mapdrawing::goto_home(bool apv){
    if(apv) place_call_list_add(cur_layer, cur_center_x, cur_center_y);
    cur_center_x = cur_world->home_x;
    cur_center_y = cur_world->home_y;
    cur_layer = cur_world->home_layer;
    draw();
}

void mapdrawing::goto_place(int pl, bool apv){
    if(apv) place_call_list_add(cur_layer, cur_center_x, cur_center_y);
    cur_center_x = -cur_world->get_place(pl)->posx;
    cur_center_y = cur_world->get_place(pl)->posy;
    cur_layer = cur_world->get_place(pl)->layer;
    draw();
}

void mapdrawing::goto_pos(int x, int y, int lay, bool apv){
    if(apv) place_call_list_add(cur_layer, cur_center_x, cur_center_y);
    cur_center_x = x;
    cur_center_y = y;
    cur_layer = lay;
    draw();
}

void mapdrawing::goto_previous(){
    if(place_call_list_num != place_call_list.begin()){
        if(place_call_list_num == place_call_list.end()){
            place_call_list_add(cur_layer, cur_center_x, cur_center_y);
            place_call_list_num--;
        }
        place_call_list_num--;
        goto_pos(place_call_list_num->x, place_call_list_num->y, place_call_list_num->layer, false);
    }
}

void mapdrawing::goto_next(){
    auto itend = place_call_list.end();
    if(place_call_list_num != --itend){
        place_call_list_num++;
        goto_pos(place_call_list_num->x, place_call_list_num->y, place_call_list_num->layer, false);
    }
}

/// -----------------------------------------------------------------------------------------------------

/// --------------------------------- menuitem funcs ----------------------------------------------------

void mapdrawing::mi_place_new_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    place_new(mapd->window, mapd->context_tile_x, mapd->context_tile_y, mapd->cur_layer, mapd->cur_world);
    mapd->draw();
}

void mapdrawing::mi_place_edit_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    place_edit(mapd->window, mapd->context_place, mapd->cur_world);
    mapd->draw();
}

void mapdrawing::mi_place_edit_marked_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    place_edit_marked(mapd->window, mapd->copy_layer, mapd->cur_world);
    mapd->copy_layer = shared_ptr<layer>(new layer(-1));
    mapd->draw();
}

void mapdrawing::mi_place_delete_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(mapd->window), (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("Do you really want to remove %s?"), mapd->context_place->name.c_str());
    gint ret = gtk_dialog_run(GTK_DIALOG(dialog));
    if(ret == GTK_RESPONSE_YES){
        for(unsigned i = 0; i < mapd->context_place->parents.size(); i++) mapd->cur_world->get_place(mapd->context_place->parents[i])->remove_subarea(mapd->context_place->id);
        mapd->cur_world->remove_place(mapd->context_place);
    }
    gtk_widget_destroy(dialog);
    (*cur_mapd)->draw();
}

void mapdrawing::mi_place_cut_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    mapd->cut_place(mapd->cur_world->get_layer(mapd->cur_layer)->get(mapd->context_tile_x, mapd->context_tile_y));
    (*cur_mapd)->draw();
}

void mapdrawing::mi_paste_place_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    mapd->paste_places(mapd->context_tile_x, mapd->context_tile_y);
    (*cur_mapd)->draw();
}

void mapdrawing::mi_subarea_new_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    int plid = dia::choose_place(mapd->window, mapd->cur_world);
    if(plid != -1) mapd->context_place->children.push_back(plid);
}

void mapdrawing::mi_subarea_new_with_layer_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    shared_ptr<place> pl = mapd->cur_world->get_layer(mapd->cur_layer)->get(mapd->context_tile_x, mapd->context_tile_y);
    int lay = mapd->cur_world->get_new_layer_id();
    mapd->goto_pos(0, 0, lay);
    shared_ptr<place> pl_new = place_new(mapd->window, 0, 0, mapd->cur_layer, mapd->cur_world);
    if(pl_new != nullptr) pl->children.push_back(pl_new->id);
    (*cur_mapd)->draw();
}

void mapdrawing::mi_subarea_goto_func(GtkMenuItem *menuitem, gpointer user_data){
    long plid = (long) user_data;
    (*cur_mapd)->goto_place(plid);
    (*cur_mapd)->draw();
}

void mapdrawing::mi_subarea_remove_func(GtkMenuItem *menuitem, gpointer user_data){
    long plid = (long) user_data;
    (*cur_mapd)->cur_world->get_place(plid)->remove_parent((*cur_mapd)->context_place->id);
    (*cur_mapd)->context_place->remove_subarea(plid);
    (*cur_mapd)->draw();
}

void mapdrawing::mi_area_new_func(GtkWidget *parent, gpointer user_data){
    new_area((*cur_mapd)->window, (*cur_mapd)->cur_world);
    (*cur_mapd)->draw();
}

void mapdrawing::mi_way_add_func(GtkMenuItem *menuitem, gpointer user_data){
    char **dir = (char**) user_data;
    if((*cur_mapd)->way_connect_pl1 == nullptr){ // wenn noch kein Ort markiert wurde
        (*cur_mapd)->way_connect_pl1 = (*cur_mapd)->context_place;
        (*cur_mapd)->way_connect_pl1_dir = string(*dir);
    } else { // wenn schon ein zweiter Ort vorhanden ist
        shared_ptr<place> pl2 = (*cur_mapd)->context_place;
        dia::place_add_way((*cur_mapd)->window, (*cur_mapd)->way_connect_pl1->id, (*cur_mapd)->way_connect_pl1_dir, pl2->id, *dir, (*cur_mapd)->cur_world);
        (*cur_mapd)->way_connect_pl1 = nullptr;
        (*cur_mapd)->draw();
    }
}

void mapdrawing::mi_way_add_another_func(GtkMenuItem *menuitem, gpointer user_data){
    const char* dir = get_str(_("Direction"), _("Type in the direction:"), "").c_str();
    if(strcmp(dir, "") != 0) mi_way_add_func(menuitem, &dir);
    (*cur_mapd)->draw();
}

void mapdrawing::mi_way_add_automatic_func(GtkMenuItem *menuitem, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    dia::auto_ways(mapd->cur_world->get_layer(mapd->cur_layer), mapd->context_place, mapd->window);
    mapd->draw();
}

void mapdrawing::mi_way_remove_func(GtkMenuItem *menuitem, gpointer user_data){
    char **dir = (char**) user_data;
    (*cur_mapd)->cur_world->remove_way((*cur_mapd)->context_place, *dir);
    gtk_widget_queue_draw((*cur_mapd)->content);
}

void mapdrawing::mi_comments_edit_func(GtkWidget *parent, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    dia::edit_comments(mapd->window, mapd->context_place);
}

void mapdrawing::mi_area_edit_func(GtkWidget *parent, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    if(mapd->context_place->area != -1) dia::edit_area(mapd->window, mapd->cur_world, mapd->context_place->area);
}

void mapdrawing::mi_insert_column_func(GtkWidget *parent, gpointer user_data){
    shared_ptr<layer> lay = (*cur_mapd)->cur_world->get_layer((*cur_mapd)->cur_layer);
    lay->insert_column((long) user_data);
    for(auto itx = lay->data.begin(); itx != lay->data.end(); itx++){
        for(auto ity = itx->second.begin(); ity != itx->second.end(); ity++){
            if(ity->second != nullptr){
                ity->second->posx = itx->first;
                ity->second->posy = ity->first;
            }
        }
    }
    (*cur_mapd)->draw();
}

void mapdrawing::mi_insert_row_func(GtkWidget *parent, gpointer user_data){
    shared_ptr<layer> lay = (*cur_mapd)->cur_world->get_layer((*cur_mapd)->cur_layer);
    lay->insert_row((long) user_data);
    for(auto itx = lay->data.begin(); itx != lay->data.end(); itx++){
        for(auto ity = itx->second.begin(); ity != itx->second.end(); ity++){
            if(ity->second != nullptr){
                ity->second->posx = itx->first;
                ity->second->posy = ity->first;
            }
        }
    }
    (*cur_mapd)->draw();
}

void mapdrawing::mi_remove_row_func(GtkWidget *parent, gpointer user_data){
    bool remove = false;

    layer test_lay = *(*cur_mapd)->cur_world->get_layer((*cur_mapd)->cur_layer);
    int places = test_lay.remove_row((long) user_data);

    if(places){
        GtkWidget *dlg = gtk_message_dialog_new(GTK_WINDOW((*cur_mapd)->window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, _("%d places will be removed!"), places);
        if(gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) remove = true;
        gtk_widget_destroy(dlg);
    } else remove = true;

    if(remove){
        shared_ptr<layer> lay = (*cur_mapd)->cur_world->get_layer((*cur_mapd)->cur_layer);
        for(typename unordered_map<int, unordered_map<int, shared_ptr<place> > >::iterator itx = lay->data.begin(); itx != lay->data.end(); itx++){
            for(typename unordered_map<int, shared_ptr<place> >::iterator ity = itx->second.begin(); ity != itx->second.end(); ity++){
                if(ity->second != nullptr && ity->first == (long) user_data) (*cur_mapd)->cur_world->remove_place(ity->second);
            }
        }
        lay->remove_row((long) user_data);
        for(typename unordered_map<int, unordered_map<int, shared_ptr<place> > >::iterator itx = lay->data.begin(); itx != lay->data.end(); itx++){
            for(typename unordered_map<int, shared_ptr<place> >::iterator ity = itx->second.begin(); ity != itx->second.end(); ity++){
                if(ity->second != nullptr){
                    ity->second->posx = itx->first;
                    ity->second->posy = ity->first;
                }
            }
        }
        (*cur_mapd)->draw();
    }
}

void mapdrawing::mi_remove_column_func(GtkWidget *parent, gpointer user_data){
    bool remove = false;

    layer test_lay = *(*cur_mapd)->cur_world->get_layer((*cur_mapd)->cur_layer);
    int places = test_lay.remove_column((long) user_data);

     if(places){
        GtkWidget *dlg = gtk_message_dialog_new(GTK_WINDOW((*cur_mapd)->window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, _("%d places will be removed!"), places);
        if(gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) remove = true;
        gtk_widget_destroy(dlg);
    } else remove = true;

    if(remove){
        shared_ptr<layer> lay = (*cur_mapd)->cur_world->get_layer((*cur_mapd)->cur_layer);
        for(typename unordered_map<int, unordered_map<int, shared_ptr<place> > >::iterator itx = lay->data.begin(); itx != lay->data.end(); itx++){
            for(typename unordered_map<int, shared_ptr<place> >::iterator ity = itx->second.begin(); ity != itx->second.end(); ity++){
                if(ity->second != nullptr && itx->first == (long) user_data) (*cur_mapd)->cur_world->remove_place(ity->second);
            }
        }
        lay->remove_column((long) user_data);
        for(typename unordered_map<int, unordered_map<int, shared_ptr<place> > >::iterator itx = lay->data.begin(); itx != lay->data.end(); itx++){
            for(typename unordered_map<int, shared_ptr<place> >::iterator ity = itx->second.begin(); ity != itx->second.end(); ity++){
                if(ity->second != nullptr){
                    ity->second->posx = itx->first;
                    ity->second->posy = ity->first;
                }
            }
        }
        (*cur_mapd)->draw();
    }
}

/// -----------------------------------------------------------------------------------------------------

/// --------------------------------- sonstiges ---------------------------------------------------------

void mapdrawing::close_func(GtkButton *button, gpointer user_data){
    mapdrawing *mapd = (mapdrawing*) user_data;
    mapd->save_meta();
    mapd->cur_world->save();
    mapd->close = true;
    if(button != nullptr){
        gint tabnum = gtk_notebook_get_n_pages(GTK_NOTEBOOK(mapd->notebook));
        for(int i = 0; i < tabnum; i++) if(gtk_notebook_get_tab_label(GTK_NOTEBOOK(mapd->notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(mapd->notebook), i)) == mapd->label){
            gtk_notebook_remove_page(GTK_NOTEBOOK((*cur_mapd)->notebook), i);
            break;
        }
    }
}

void mapdrawing::place_call_list_add(int lay, float x, float y){
    if(place_call_list_num != place_call_list.end()){
        auto it = place_call_list_num;
        it++;
        place_call_list.erase(it, place_call_list.end());
    }
    coord newc = coord(lay, x, y);
    coord lc = place_call_list.back();
    if(newc != lc) place_call_list.push_back(newc);
    place_call_list_num = place_call_list.end();
}

void mapdrawing::place_call_list_add_cur_place(){
    place_call_list_add(cur_layer, cur_center_x, cur_center_y);
}

void mapdrawing::draw(){
    if(content != nullptr) gtk_widget_queue_draw(content);
}

void mapdrawing::set_home(){
    cur_world->home_layer = cur_layer;
    cur_world->home_x = cur_center_x;
    cur_world->home_y = cur_center_y;
}

void goto_place(int plid, void* md){
    shared_ptr<mapdrawing> *mapd = (shared_ptr<mapdrawing>*) md;
    (*mapd)->goto_place(plid, false);
}

void mapdrawing::cut_place(shared_ptr<place> pl){
    copy_layer = shared_ptr<layer>(new layer(cur_layer));
    copy_layer->set(pl, pl->posx, pl->posy);
    copy_ofs_x = pl->posx;
    copy_ofs_y = pl->posy;
}

void mapdrawing::cut_add_place(shared_ptr<place> pl){
    if(copy_layer->id == -1){
        copy_layer->id = cur_layer;
        copy_ofs_x = pl->posx;
        copy_ofs_y = pl->posy;
    }
    if(copy_layer->id == cur_layer){
        copy_layer->set(pl, pl->posx, pl->posy);
    }
}

bool mapdrawing::paste_places(int x, int y){
    bool move = true;
    int place_cnt = 0;
    shared_ptr<layer> cl = cur_world->get_layer(cur_layer);
    if(copy_layer->id != -1){
        for(unordered_map<int, unordered_map<int, shared_ptr<place> > >::iterator itx = copy_layer->data.begin(); itx != copy_layer->data.end(); itx++){
            for(unordered_map<int, shared_ptr<place> >::iterator ity = itx->second.begin(); ity != itx->second.end(); ity++){
                if(ity->second != nullptr && cur_world->get_place(ity->second->id) != nullptr){
                    place_cnt++;
                    if(cl->get(itx->first - copy_ofs_x + x, ity->first - copy_ofs_y + y) != nullptr){// && copy_layer->get(itx->first - copy_ofs_x + x, ity->first - copy_ofs_y + y) == nullptr){
                        move = false;
                        break;
                    }
                }
            }
        }

        // Sicherheitsfrage
        if(place_cnt > 1 && move){
            GtkWidget *dlg = gtk_message_dialog_new(GTK_WINDOW(window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, _("Do you want to move %d places?"), place_cnt);
            if(gtk_dialog_run(GTK_DIALOG(dlg)) != GTK_RESPONSE_OK) move = false;
            gtk_widget_destroy(dlg);
        }

        if(move){
            for(unordered_map<int, unordered_map<int, shared_ptr<place> > >::iterator itx = copy_layer->data.begin(); itx != copy_layer->data.end(); itx++){
                for(unordered_map<int, shared_ptr<place> >::iterator ity = itx->second.begin(); ity != itx->second.end(); ity++){
                    if(ity->second != nullptr && cur_world->get_place(ity->second->id) != nullptr){
                        cur_world->move_place(ity->second, itx->first - copy_ofs_x + x, ity->first - copy_ofs_y + y, cur_layer);
                    }
                }
            }
            copy_layer = shared_ptr<layer>(new layer(-1));
        }
    }
    return move;
}

}
